<?php

/**
 * @package PHP Lazy Loading
 * @author Romain Follet <phplazyloading@cooldev.net>
 * @licence http://opensource.org/licenses/mit-license.php
 * @link http://code.google.com/p/phplazyloading/
 */
class Lazy {
	/**
	 * Some private array
	 * 
	 * @access private
	 * @static
	 * @var array
	 */
	private static $_functions = array ();
	private static $_callback = array ();
	private static $_extensions = array ('php', 'inc');
	
	/**
	 * Call an undefined function with arguments (PHP >= 5.3)
	 * 
	 * @alias _run
	 * @access public
	 * @static
	 * @param string $function
	 * @param array $arguments
	 * @return mixed
	 */
	public static function __callStatic ($function, $arguments) {
		return self::_run ($function, $arguments);
	}
	
	
	/**
	 * Call an undefined function with arguments (PHP >= 5)
	 * 
	 * @alias _run
	 * @access public
	 * @param string $function
	 * @param array $arguments
	 * @return mixed
	 */
	public function __call ($function, $arguments) {
		return self::_run ($function, $arguments);
	}
	
	
	/**
	 * Call an undefined function with arguments (PHP >= 5)
	 * 
	 * @alias _run
	 * @access public
	 * @static
	 * @param string $function
	 * @params mixed $arguments
	 * @return mixed
	 */
	public static function call ($function) {
		$arguments = func_get_args ();
		$arguments = array_slice ($arguments, 1);
		return self::_run ($function, $arguments);
	}
	
	
	/**
	 * Call an undefined function with arguments
	 * 
	 * @access private
	 * @static
	 * @param string $function
	 * @param array $arguments
	 * @return mixed
	 */
	private static function _run ($function, $arguments) {
		if (!in_array ($function, array_keys (self::$_functions))) {
			$filename = self::_callback ($function);
			if ($filename) {
				self::$_functions[$function] = $filename;
			} else {
				self::$_functions[$function] = false;
				throw new Exception ('Unable to lazy load function <strong>' . $function . '</strong>.');
			}
		}
		
		$filename = self::$_functions[$function];
		if ($filename !== false) {
			if (!function_exists ($function)) {
				require_once $filename;
			}
			return call_user_func_array ($function, $arguments);
		}
		
		return null;
	}
	
	
	/**
	 * Register an user function or an user class that return filename
	 * 
	 * @access public
	 * @static
	 * @param mixed $callback function name or classe array
	 * @return boolean
	 */
	public static function register ($callback) {
		if (is_callable ($callback)) {
			self::$_callback[] = $callback;
			self::$_callback = array_unique (self::$_callback);
			return true;
		}
		return false;
	}
	
	
	/**
	 * Unregister a callback
	 * 
	 * @access public
	 * @static
	 * @param mixed $callback function name or class array
	 * @return boolean
	 */
	public static function unregister ($callback) {
		if (($key = array_search ($callback, self::$_callback, true)) !== false) {
			unset (self::$_callback[$key]);
			return true;
		}
		return false;
	}
	
	
	/**
	 * Determine function's filename with registered callback function. Or default
	 * callback
	 * 
	 * @access private
	 * @static
	 * @param string function's name
	 * @return mixed Filename or false
	 */
	private static function _callback ($function) {
		if (is_array (self::$_callback)) {
			foreach (self::$_callback as $callback) {
				$filename = call_user_func ($callback, $function);
				if ($filename) {
					return $filename;
				}
			}
		}
		return self::_defaultCallback ($function);
	}
	
	
	/**
	 * Default callback function
	 * 
	 * @access private
	 * @static
	 * @param string $function function's name
	 * @return mixed File's path or false
	 */
	private static function _defaultCallback ($function) {
		if (isset ($_SERVER['PATH'])) {
			$paths = explode (':', $_SERVER['PATH']);
		} else {
			$paths = array ('.');
		}
		
		foreach ($paths as $path) {
			if (substr ($path, -1) != '/') {
				$path .= '/';
			}
			
			foreach (self::$_extensions as $extension) {
				$filename = $path . $function . '.' . $extension;
				
				if (is_file ($filename)) {
					if (is_readable ($filename)) {
						return $filename;
					}
				}
			}
		}
		return false;
	}
	
	
	/**
	 * Set default extensions for the default callback function
	 * 
	 * @access public
	 * @static
	 * @param string $extensions List of extensions with comma separator.
	 * @return string List of extensions with comma separator
	 */
	public static function extensions ($extensions = null) {
		if (!s_null ($extensions)) {
			self::$_extensions = explode (',', $extensions);
		}
		return implode (',', self::$_extensions);
	}
	
	
	/**
	 * Get list of callback.
	 * 
	 * @access public
	 * @static
	 * @return array
	 */
	public static function functions () {
		if (!empty (self::$_callback)) {
			return self::$_callback;
		}
		return false;
	}
}

/*************************************/
/** NON OBJECT SCRIPT COMPATIBILITY **/
/*************************************/


/**
 * Lazy register method
 * 
 * @alias Lazy::register
 * @param mixed $callback String for function name, array for method name, or anonym function
 * @return boolean
 */
function lazy_register ($callback) {
	return Lazy::register ($callback);
}


/**
 * Lazy unregister method
 * 
 * @alias Lazy::unregister
 * @param mixed $callback String for function name, array for method name, or anonym function
 * @return boolean
 */
function lazy_unregister ($callback) {
	return Lazy::unregister ($callback);
}


/**
 * Set default extensions for the default callback function
 * 
 * @alias Lazy::extensions
 * @param string $extensions List of extensions with comma separator.
 * @return string List of extensions with comma separator
 */
function lazy_extensions ($extensions = null) {
	return Lazy::extensions ($extensions);
}


/**
 * Get list of callback.
 * 
 * @alias Lazy::functions
 * @return array
 */
function lazy_functions () {
	return Lazy::functions ();
}


/**
 * Call an undefined function with arguments
 * 
 * @alias Lazy::call
 * @param string $function
 * @param array $arguments
 * @return mixed
 */
function lazy_call ($function) {
	$arguments = func_get_args ();
	$arguments = array_slice ($arguments, 1);
	return call_user_func_array (array ('Lazy', 'call'), $arguments);
}